Calendar = function(ct, taskId, width, height, nav){
	this.nav = nav;
	this.secondsPerPix = 36;
	this.picWidth = 250;
	this.picHeight = 175;
	this.imgSpacing = 10;
	this.ct = ct;
	ct.addClass('cal-wrap');
	
	this.taskId = taskId; //
	
	this.currentDay = moment().startOf('day');
	this.resize(width, height);
}

Calendar.prototype.resize = function(width, height){
	width = width - 2; //2 border
	this.lastIndex = -1; //image line index
	
	// memo panel
	this.memoHidden = true;
	
	this.ct.width(width).height(height); 
	this.init(width, height);
	this.populate();
}

Calendar.prototype.setTaskId = function(taskId){
	this.taskId = taskId;
}

Calendar.prototype.setDay = function(day){
	this.currentDay = day;
	this.populate();
}

Calendar.prototype.populate = function(){
	this.ct.find('.cal-day').text(this.currentDay.format('dddd, MMM Do YYYY'));
	this.loadData();
}

Calendar.prototype.init = function(width, height){
	//scroll bar width is 17;
	var bodyHeight = height - 20 - 10*2; // 20 is head height; 10 is its padding
	var leftWidth = 80;
	var middleWidth = width - leftWidth  - 17;
	var pixesPerHour = (3600 / this.secondsPerPix).toFixed(0);
	
	var xCount = Math.floor((middleWidth - 50) / (this.picWidth + this.imgSpacing + 2));
	var yCount = Math.floor((bodyHeight -  20) / (this.picHeight + this.imgSpacing));
	this.xCount = xCount;
	this.yCount = yCount;
	var imgPanelWidth = xCount * (this.picWidth + this.imgSpacing + 2); //50 is cal-period width
	var imgPanelHeight = yCount * (this.picHeight + this.imgSpacing + 2);
	var imgPanelPaddingLeft = (middleWidth - 50 - imgPanelWidth) / 2 + 50;
	var innerPaddingTop = Math.ceil(bodyHeight / 2);
	this.innerPaddingTop = innerPaddingTop;
	var innerPaddingBottom = innerPaddingTop - 17;
	
	var me = this;
	var html = [];
	
	//head
	html.push('<div class="cal-head">');
	html.push('<div class="cal-total"></div>');
	if(this.nav == true){
		html.push('<span class="cal-prev">&lt;</span>');
	}
	html.push('<span class="cal-day"></span>');
	if(this.nav == true){
		html.push('<span class="cal-next">&gt;</span>');
	}
	html.push('</div>');
	
	//body wrapper
	html.push('<div class="cal-body-wapper">');
	//memo panel in wrapper div
	html.push('<div id="memo-panel"></div>');
	
	//body
	html.push('<div class="cal-body" style="height:'+bodyHeight+'px">');
	//inner
	html.push('<div class="cal-inner" style="padding:'+ innerPaddingTop +'px 0 '+innerPaddingBottom+'px 0;">');
	//left, time bar
	html.push('<div class="cal-left" style="width:'+leftWidth+'px">');
	
	for (var i = 0; i < 24; i++) {
		var time = i > 9 ? i : ("0" + i);
		html.push('<div class="cal-hour" cal-data="' + i + '" style="height:'+(pixesPerHour-1)+'px">' + time + ':00</div>');
	}
	html.push('</div>');
	
	//middle
	html.push('<div class="cal-middle"  style="width:'+middleWidth+'px">');
	var calGrid = '<div class="cal-grid" style="height:'+(pixesPerHour-1)+'px"></div>';
	for (var i = 0; i < 24; i++) {
		html.push(calGrid);
	}
	
	html.push('</div>');
	
	//inner close
	html.push('</div>');
	
	//img panel in wrapper div
	html.push('<div id="img-panel" style="left:'+imgPanelPaddingLeft+'px;height:'+imgPanelHeight+'px;width:'+imgPanelWidth+'px;"></div>');
	//body close
	html.push('</div>');
	
	//time line
	html.push('<div id="time-line" style=""></div>');
	//body-wapper close
	html.push('</div>');
	
	//big img
	html.push('<div id="img-big"><img><div class="img-label"></div><div class="btn-close"></div></div>');
	
	
	this.ct.html(html.join(''));
	
	//init components;
	this.imgPanel = $('#img-panel');
	this.memoPanel = $('#memo-panel');
	this.imgBig = $('#img-big');
	this.calBody = $('.cal-body');
	this.hoursLabel = $('.cal-total');
	
	//add event
	this.ct.find('.cal-prev').click(function(){
		me.currentDay.subtract(1, 'days');
		me.populate();
		$('.dp-prev').click();
	});
	this.ct.find('.cal-next').click(function(){
		me.currentDay.add(1, 'days');
		me.populate();
		$('.dp-next').click();
	});
	this.memoPanel.click(function(){
		me.showMemo('');
	})
	
	
	this.last = 0;
	this.calBody.scroll(function(){
		if(top == me.last) return false;
		var data = me.data;
		var top = this.scrollTop;
		var down = top > me.last;
		
		me.imgPanel.css('top', (20 + top) + 'px');
		
		//time
		for (var i = 0; i < data.times.length; i++) {
			var t = data.times[i];
			var bottom = t.top + t.height;
			if(down){
				// entry
				if(t.top >= me.last && t.top <= top){
//					console.log(top/60);
				}
				if(bottom >= me.last && bottom <= top){
//					console.log(top/60);
				}
				// leave
			}else{ //up
				//entry
				if(bottom <= me.last && bottom > top){
//					console.log(top/60);
				}
				// leave
				if(t.top <= me.last && t.top > top){
//					console.log(top/60);
				}
			}
		}
		
		//memo 
		var foundMemo = false;
		for (var i = 0; i < data.memos.length; i++) {
			var m = data.memos[i];
			if(top >= m.top && top <= m.bottom){
				foundMemo = true;
				me.showMemo(m.memo);
				break;
			}
		}
		if(foundMemo === false){
			me.showMemo('');
		}
		
		//img
		for (var i = 0; i < me.lineStatus.length; i++) {
			if(me.lineStatus[i]){
				var p = me.lineStatus[i].pos;
				//judge p is between top and me.last
				if(((p > top && p > me.last) || (p < top && p < me.last)) === false){
					me.renderImages(i);
					break;
				}
			}
		}
		
		me.last = top;
		return false;
	})
	
	
	var imgBig = this.imgBig;
	imgBig.find('div.btn-close').click(function(){
		imgBig.hide();
	});
	
	imgBig.click(function(){
		return false;
	});
	$(document).click(function(){
		imgBig.hide();
	}).keydown(function(event){
		if(event.which == 27){
			imgBig.hide();
		}
	});
}

/*
 * Fetch data of this day from server 
 */
Calendar.prototype.loadData = function(){
	var me = this;
	$.get("/traktask/cal/daily", {
		start: this.currentDay.unix() * 1000,
		taskId: this.taskId
	}, function(response){
		me.fillData(response);
	}, 'json');
}


Calendar.prototype.createPeroidTitle = function(t){
	return ((t.end - t.start) / 3600).toFixed(1) + " h";
}

/*
 * Make sure all data are order by date
 */
Calendar.prototype.fillData = function(data){
	this.data = data;
	var spp = this.secondsPerPix;
	var me = this;
	
	this.hoursLabel.text((data.seconds / 3600).toFixed(1) + " hours");
	
	//clear data first
	$('.cal-period, .memo-bar, .img-line').remove();
	
	//to fill
	if(data.times.length == 0){
		console.log("no data");
		return;
	}
	
	//check time data if ordered
	var d = data.times[0].start;
	var dayStart = this.currentDay.unix();
	var dayEnd = this.currentDay.clone().endOf('day').unix();
	if(d < dayStart){
		d = dayStart;
		data.times[0].start = d;
	}
	if(data.times[data.times.length - 1].end > dayEnd){
		data.times[data.times.length - 1].end = dayEnd;
	}
	var invalid = false;
	//check time data if ordered, and calculate start and end.
	for (var i = 0; i < data.times.length; i++) {
		var t = data.times[i];
		t.top = ((t.start - dayStart) / spp);
		t.height = ((t.end - t.start) / spp);
		if(t.start < d){
			invalid = true;
			console.log("Invalid time data: i=" + t.id);
		}
		d = t.start;
		if(t.end < d){
			invalid = true;
			console.log("Invalid time data: i=" + t.id);
		}
		d = t.end;
	}
	
	//check memo data if ordered, 
	d = 0;
	for (var i = 0; i < data.memos.length; i++) {
		var m = data.memos[i];
		m.top = ((m.start - dayStart) / spp);
		m.bottom = ((m.end - dayStart) / spp);
		m.memo = m.memo.replace(/</g, '&lt;').replace(/>/g, '&gt;');
		var t = m.start;
		if(t < d){
			invalid = true;
			console.log("Invalid memo data: i=" + data.memos[i].id);
		}
		d = t;
	}
	//tidy memo data
	var x = 0;
	for (var i = 0; i < data.times.length; i++) {
		var t = data.times[i];
		var memos = [];
		for (; x < data.memos.length; x++) {
			var m = data.memos[x];
			if(m.start >= t.start && m.start <= t.end){
				memos.push(m);
			}else{
				break;
			}
		}
		if(memos.length > 0){
			for (var y = 0; y < memos.length - 1; y++) {
				var _m0 = memos[y];
				var _m1 = memos[y + 1];
				_m0.end = _m1.start;
			}
			memos[memos.length - 1].end = t.end;
		}
	}
	//and calculate start and end.
	for (var i = 0; i < data.memos.length; i++) {
		var m = data.memos[i];
		m.top = ((m.start - dayStart) / spp);
		m.bottom = ((m.end - dayStart) / spp);
	}
	
	//check screenshot data if ordered, and calculate start.
	d = 0;
	for (var i = 0; i < data.screenshots.length; i++) {
		var ss = data.screenshots[i];
		ss.top = ((ss.start - dayStart) / spp);
		var t = ss.start;
		if(t < d){
			invalid = true;
			console.log("Invalid screenshot data: i=" + data.screenshots[i].id);
		}
		d = t;
	}
	
	if(invalid === false){
		//draw
		var html = [];
		//cal period
		for (var i = 0; i < data.times.length; i++) {
			var t = data.times[i];
			var top = t.top + this.innerPaddingTop;
			var height = t.height;
			html.push('<div id="p_' + i + '" class="cal-period" style="top:'+top.toFixed(0)+'px;height:'+height.toFixed(0)+'px">');
			var title = this.createPeroidTitle(t);
			html.push(title);
			html.push('</div>');
		}
		
		
		$(".cal-middle").append(html.join(''));
		this.showImageListPanel(data.screenshots, data.times);
		
		this.calBody.scrollTop(data.times[0].top + 1);
	}
}


/*
 * Screenshots should be populated in xCount * 24 cells.
 * Generally, it produces 4 images per hour. So total count would be 4 * 24 = 96;
 */
Calendar.prototype.showImageListPanel = function(screenshots, times){
	var xCount = this.xCount;
	var imgLine = "<div class='img-line' style='margin-bottom:"+this.imgSpacing+"px;height:"+(this.picHeight + 1)+"px;'>";
	this.lineStatus = []; // record each image line's position
	
	var html = [];
	var i = 0;
	for (var x = 0; x < times.length; x++) {
		var peroid = times[x];
		var imgCount = 0;
		var needClose = false;
		for (; i < screenshots.length; i++) {
			var ss = screenshots[i];
			if(ss.start >= peroid.start && ss.start <= peroid.end){
				if(imgCount % xCount == 0){
					needClose = true;
					html.push(imgLine)
					this.lineStatus.push({
						pos: ss.top,
						rendered: false,
						hidden: true
					});
				}
				html.push("<img style='width:"
						+this.picWidth
						+ "px;height:"
						+ this.picHeight
						+ "px;margin-right:"
						+ this.imgSpacing+"px' data-src='/traktask/cal/img?id=" 
						+ ss.id + "'data-time='"
						+ moment.unix(ss.start).format('YYYY-MM-DD HH:mm:ss')
						+ "'>");
				imgCount++;
			}else{
				i++;
				break;
			}
			if(needClose && (imgCount % xCount == 0)){
				needClose = false;
				html.push("</div>");
			}
		}
		if(needClose == true){
			html.push("</div>");
		}
		
		//create a empty image line
		if(x != times.length - 1){
			this.lineStatus.push(null);
			html.push(imgLine);
			html.push("</div>");
		}
		
		i--;
	}
	
	var me = this;
	$("#img-panel").html(html.join(''))
		.find("img")
		.click(function(){
			var it = $(this);
			var src = it.attr('data-src') + "&s=f";
			var time = it.attr('data-time');
			me.showImagePanel(src, time);
			return false;
		});
	if(this.lineStatus.length != $(".img-line").size()){
		alert("line pos incorrect");
	}
}

Calendar.prototype.renderImages = function(index){
	if(index == this.lastIndex){
		return;
	}
	this.lastIndex = index;
	
	$('.img-line').hide();
	if(index > 0){
		index --;
	}
	for (var i = 0; i < this.yCount; i++) {
		this.renderEachLine(i + index);
	}
}

Calendar.prototype.renderEachLine = function(index){
	if(index == this.lineStatus.length){
		return;
	}
	var imgLine = this.lineStatus[index];
	
	var line = $($('.img-line').get(index));
	if(imgLine && imgLine.rendered === false){
		imgLine.rendered = true;
		line.find('img').each(function(){
			var img = $(this);
			img.attr('src', img.attr('data-src'));
		});
	}
	line.show();
	
}

Calendar.prototype.showImagePanel = function(url, time){
	var imgBig = this.imgBig;
	imgBig.find('img').attr('src', url);
	imgBig.find('.img-label').text(time);
	imgBig.show();
}

Calendar.prototype.showMemo = function(memo){
	var memoPanel = this.memoPanel;
	if(memo){
		memoPanel.html("<pre>" + memo + "</pre>");
	}
	if(this.memoHidden === true){
		if(memo){
			this.memoHidden = false;
			memoPanel.slideDown();
		}
	}else{
		if(!memo){
			this.memoHidden = true;
			memoPanel.slideUp(function(){
				memoPanel.html('');
			});
		}
	}
}